package com.qen.encrypt.configuration;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.StrUtil;
import com.qen.encrypt.exception.EncryptException;
import com.qen.encrypt.inter.Decrypt;
import com.qen.encrypt.inter.EncryptGroup;
import com.qen.encrypt.properties.EncryptMapConfig;
import com.qen.encrypt.utils.ParamEncrypt;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdviceAdapter;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;

/**
 * 对请求内容进行解密
 */
@ControllerAdvice
public class DecryptRequest extends RequestBodyAdviceAdapter {

    private final Logger log = LoggerFactory.getLogger(getClass());
    private EncryptMapConfig encryptMapConfig;


    public DecryptRequest(@Qualifier("encryptMapConfig") EncryptMapConfig encryptMapConfig) {
        this.encryptMapConfig = encryptMapConfig;

    }

    @Override
    public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        return methodParameter.hasMethodAnnotation(Decrypt.class) ||
                methodParameter.hasParameterAnnotation(Decrypt.class);
    }

    @Override
    public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter,
                                           Type targetType, Class<? extends HttpMessageConverter<?>> converterType)
            throws IOException {
        ParamEncrypt encrypt = getGroupKey(parameter);
        InputStream is = null;
        ByteArrayOutputStream baos = null;
        try {
            is = inputMessage.getBody();
            baos = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024]; // 缓冲区大小
            int len;
            while ((len = is.read(buffer)) > -1) {
                baos.write(buffer, 0, len);
            }
            baos.flush();
            byte[] body = baos.toByteArray();
            // log.info("bodyString={}", new String(body));
            ByteArrayInputStream bais = new ByteArrayInputStream(encrypt.priDecrypt(Base64.decode(body)));
            return new HttpInputMessage() {
                @Override
                public InputStream getBody() {
                    return bais;
                }

                @Override
                public HttpHeaders getHeaders() {
                    return inputMessage.getHeaders();
                }
            };
        } catch (Exception e) {
            e.printStackTrace();
            throw new EncryptException(e);
        } finally {
            is.close();
            baos.close();
        }
    }


    private ParamEncrypt getGroupKey(MethodParameter parameter) {
        Class<?> declared = parameter.getMethod().getDeclaringClass();
        if (parameter.hasMethodAnnotation(EncryptGroup.class)) {
            String methodGroup = parameter.getMethodAnnotation(EncryptGroup.class).value();
            if (StrUtil.isNotBlank(methodGroup)) {
                return encryptMapConfig.getParamEncrypt(methodGroup);
            } else {
                return encryptMapConfig.getParamEncrypt();
            }
        } else if (declared.isAnnotationPresent(EncryptGroup.class)) {
            String group = declared.getAnnotation(EncryptGroup.class).value();
            if (StrUtil.isNotBlank(group)) {
                return encryptMapConfig.getParamEncrypt(group);
            } else {
                return encryptMapConfig.getParamEncrypt();
            }
        }
        return encryptMapConfig.getParamEncrypt();
    }
}
